package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"mall/common/modelx"
	"mall/service/setting/model"

	// "github.com/volatiletech/null/v8"
	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	// SettingRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customSettingRepository.
	SettingRepository interface {
		Conn() error
		FindOne(ctx context.Context, id int64, cols []string) (*model.Setting, error)
		GetList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]interface{},
		) ([]*model.Setting, int64, error)
		Insert(ctx context.Context, setting *model.Setting) error
		Update(ctx context.Context, setting *model.Setting) (int64, error)
		DeleteBatch(ctx context.Context, ids []uint64) (int64, error)
	}

	customSettingRepository struct {
		cacheConf  cache.CacheConf
		dataSource string

		mysqlConn *sql.DB
	}
)

// NewSettingRepository returns a repository for the database table.
func NewSettingRepository(dataSource string, cacheConf cache.CacheConf) SettingRepository {
	return &customSettingRepository{
		cacheConf:  cacheConf,
		dataSource: dataSource,
	}
}

func (r *customSettingRepository) Conn() error {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return err
		}

		r.mysqlConn = mysqlConn
	}

	return nil
}

func (r *customSettingRepository) FindOne(
	ctx context.Context,
	id int64,
	cols []string,
) (term *model.Setting, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	term, err = model.FindSetting(ctx, r.mysqlConn, uint64(id), cols...)

	return
}

func (r *customSettingRepository) GetList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]interface{},
) (list []*model.Setting, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}
	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.SettingColumns.SettingName:
				mods = append(mods, modelx.GetStringMods(model.SettingColumns.SettingName, opMap)...)
			case model.SettingColumns.Autoload:
				mods = append(mods, modelx.GetBooleanMods(model.SettingColumns.Autoload, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.Settings(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.SettingColumns.SettingID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.Settings(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customSettingRepository) Insert(ctx context.Context, setting *model.Setting) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	err = setting.Insert(ctx, r.mysqlConn, boil.Infer())

	return
}

func (r *customSettingRepository) Update(ctx context.Context, setting *model.Setting) (int64, error) {
	if err := r.Conn(); err != nil {
		return 0, err
	}

	settingId, err := setting.Update(ctx, r.mysqlConn, boil.Infer())
	if err != nil {
		return 0, err
	}

	return settingId, nil
}

func (r *customSettingRepository) DeleteBatch(ctx context.Context, ids []uint64) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]interface{}, 0, len(ids))
		for _, id := range ids {
			values = append(values, id)
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.SettingColumns.SettingID), values...))
	}

	rowsAff, err = model.Settings(mods...).DeleteAll(ctx, r.mysqlConn)
	return
}
